﻿namespace ProcessMonitor
{
    using System;
    using System.Collections.ObjectModel;
    using System.Diagnostics;
    using System.Linq;
    using System.Windows;
    using System.Windows.Data;
    using System.Windows.Threading;
    using System.Windows.Controls;
    using System.ComponentModel;
    using System.Collections.Generic;
    using ProcessMonitor.Helpers;

    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public CollectionViewSource ProcessViewSource { get; set; }
        public CollectionViewSource ThreadsViewSource { get; set; }
        public ObservableCollection<Process> AllProcess { get; set; }
        public ObservableCollection<ProcessThread> AllThreads { get; set; }
        public DispatcherTimer RefreshProcessTimer { get; set; }
        public Process CurrentProcess { get; set; }
        public PerformanceCounter PerformanceCounterRead { get; set; }
        public PerformanceCounter PerformanceCounterWrite { get; set; }
        public IDictionary<string, string> AppPoolNames { get; set; }

        private double _total;
        public double Total
        {
            get { return _total; }
            set
            {
                _total = value;
                NotifyPropertyChanged("Total");
            }
        }

        public MainWindow()
        {
            InitializeComponent();

            this.AppPoolNames = Utils.GetApplicationPoolNames();

            this.AllProcess = new ObservableCollection<Process>(Process.GetProcesses().OrderBy(x => x.ProcessName));
            this.ProcessViewSource = new CollectionViewSource();
            this.ProcessViewSource.Source = this.AllProcess;
            this.RefreshProcessTimer = new DispatcherTimer(DispatcherPriority.Background);
            this.RefreshProcessTimer.Interval = TimeSpan.FromMilliseconds(500);
            this.RefreshProcessTimer.Tick += UpdateProcessList;
            this.RefreshProcessTimer.Start();

            this.DataContext = this;

            // WARMUP CONFIGURATION
            ConfigWindow cw = ConfigWindow.Instance;
        }

        private void UpdateProcessList(object sender, EventArgs e)
        {
            // UPDATE PROCESS COLLECTION
            this.AllProcess = new ObservableCollection<Process>(Process.GetProcesses().OrderBy(x => x.ProcessName));
            this.ProcessViewSource.View.Refresh();
            this.UpdateProcessDetails();

            this.UpdateTotal();
        }

        private void UpdateTotal()
        {
            if (this.CurrentProcess == null || string.IsNullOrEmpty(txtProcessTime.Text))
            {
                this.Total = 0;
                return;
            }

            double processingPrice = ConfigWindow.Instance.Processing;
            double bytesReadPrice = ConfigWindow.Instance.BytesRead;
            double bytesWritePrice = ConfigWindow.Instance.BytesWrite;

            double processingSeconds = TimeSpan.Parse(txtProcessTime.Text).TotalSeconds;
            long bytesRead = PerformanceCounterRead.RawValue;
            long bytesWrite = PerformanceCounterWrite.RawValue;

            this.Total = (bytesRead * bytesReadPrice) +
                         (bytesWrite * bytesWritePrice) +
                         (processingSeconds * processingPrice);
        }

        private void UpdateProcessDetails()
        {
            if (listViewProcess.SelectedItem == null)
            {
                return;
            }

            txtProcessId.GetBindingExpression(TextBox.TextProperty).UpdateTarget();
            txtProcessThreads.GetBindingExpression(TextBox.TextProperty).UpdateTarget();
            txtProcessMemory.GetBindingExpression(TextBox.TextProperty).UpdateTarget();
            txtProcessTime.GetBindingExpression(TextBox.TextProperty).UpdateTarget();
            txtProcessDiskRead.GetBindingExpression(TextBox.TextProperty).UpdateTarget();
            txtProcessDiskWrite.GetBindingExpression(TextBox.TextProperty).UpdateTarget();
            txtProcessAppPool.Text = this.AppPoolNames.ContainsKey(txtProcessId.Text) ? this.AppPoolNames[txtProcessId.Text] : string.Empty;

            if (this.ThreadsViewSource == null)
            {
                return;
            }

            this.ThreadsViewSource.View.Refresh();

            txtThreadId.GetBindingExpression(TextBox.TextProperty).UpdateTarget();
            txtThreadPriority.GetBindingExpression(TextBox.TextProperty).UpdateTarget();
            txtThreadState.GetBindingExpression(TextBox.TextProperty).UpdateTarget();
            txtThreadTime.GetBindingExpression(TextBox.TextProperty).UpdateTarget();
        }

        private void listViewProcess_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (listViewProcess.SelectedItem == null)
            {
                // CLEAN CURRENT THREADS
                this.AllThreads = new ObservableCollection<ProcessThread>();
                this.ThreadsViewSource = new CollectionViewSource();
                this.ThreadsViewSource.Source = this.AllThreads;
                this.ThreadsViewSource.View.Refresh();
                this.listViewThreads.ItemsSource = ThreadsViewSource.View;

                this.CurrentProcess = null;
                this.PerformanceCounterRead = null;
                this.PerformanceCounterWrite = null;

                return;
            }

            // UPDATE CURRENT PROCESS
            this.CurrentProcess = listViewProcess.SelectedItem as Process;

            this.AppPoolNames = Utils.GetApplicationPoolNames();

            // UPDATE PERFORMANCE COUNTER
            this.PerformanceCounterRead = new PerformanceCounter("Process", "IO Read Bytes/sec", this.CurrentProcess.ProcessName);
            this.PerformanceCounterWrite = new PerformanceCounter("Process", "IO Write Bytes/sec", this.CurrentProcess.ProcessName);
            
            // UPDATE THREADS
            this.AllThreads = new ObservableCollection<ProcessThread>((listViewProcess.SelectedItem as Process).Threads.Cast<ProcessThread>());
            this.ThreadsViewSource = new CollectionViewSource();
            this.ThreadsViewSource.Source = this.AllThreads;
            this.ThreadsViewSource.View.Refresh();
            this.listViewThreads.ItemsSource = ThreadsViewSource.View;
        }

        private void UpdateButton_Click(object sender, RoutedEventArgs e)
        {
            // GET ALL PROCESS AND UPDATE VIEW
            this.AllProcess = new ObservableCollection<Process>(Process.GetProcesses().OrderBy(x => x.ProcessName));
            this.ProcessViewSource = new CollectionViewSource();
            this.ProcessViewSource.Source = this.AllProcess;
            this.listViewProcess.ItemsSource = this.ProcessViewSource.View;
        }

        private void NotifyPropertyChanged(string p)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(p));
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void ConfigButton_Click(object sender, RoutedEventArgs e)
        {
            ConfigWindow.Instance.ShowDialog();
        }
    }
}
